package pkg

import (
	"bytes"
	"crypto/aes"
	"crypto/cipher"
	"encoding/base64"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"net/http"
	"time"
)

// 封装前端传入的数据
type ConfirmLoginDto struct {
	PhoneData string `json:"phone_data"`
	Iv        string `json:"iv"`
	WxCode    string `json:"wx_code"`
}

// 封装code2session接口返回数据
type WxSessionKeyDto struct {
	OpenId     string `json:"openid"`
	SessionKey string `json:"session_key"`
	UnionId    string `json:"unionid"`
	ErrCode    int    `json:"errcode"`
	ErrMsg     string `json:"errmsg"`
}

// 封装手机号信息数据
type WxPhoneDto struct {
	PhoneNumber     string `json:"phoneNumber"`
	PurePhoneNumber string `json:"purePhoneNumber"`
	CountryCode     string `json:"countryCode"`
}

type Wechat struct {
	AppId     string
	AppSecret string
}

const SESSIONKEY_URL = "https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code"

func (t Wechat) get(url string) (int, []byte) {
	client := &http.Client{Timeout: 5 * time.Second}
	resp, err := client.Get(url)
	if err != nil {
		log.Printf("error sending GET request, url: %s, %q", url, err)
		return http.StatusInternalServerError, nil
	}
	defer resp.Body.Close()
	var buffer [512]byte
	result := bytes.NewBuffer(nil)
	for {
		n, err := resp.Body.Read(buffer[0:])
		result.Write(buffer[0:n])
		if err != nil {
			if err == io.EOF {
				break
			}
			log.Printf("error decoding response from GET request, url: %s, %q", url, err)
		}
	}
	return resp.StatusCode, result.Bytes()
}

func (t Wechat) AesDecrypt(encryptedData, sessionKey, iv string) ([]byte, error) {
	//Base64解码
	keyBytes, err := base64.StdEncoding.DecodeString(sessionKey)
	if err != nil {
		return nil, err
	}
	ivBytes, err := base64.StdEncoding.DecodeString(iv)
	if err != nil {
		return nil, err
	}
	cryptData, err := base64.StdEncoding.DecodeString(encryptedData)
	if err != nil {
		return nil, err
	}
	origData := make([]byte, len(cryptData))
	//AES
	block, err := aes.NewCipher(keyBytes)
	if err != nil {
		return nil, err
	}
	//CBC
	mode := cipher.NewCBCDecrypter(block, ivBytes)
	//解密
	mode.CryptBlocks(origData, cryptData)
	//去除填充位
	origData = t.PKCS7UnPadding(origData)
	return origData, nil
}

func (t Wechat) PKCS7UnPadding(plantText []byte) []byte {
	length := len(plantText)
	if length > 0 {
		unPadding := int(plantText[length-1])
		return plantText[:(length - unPadding)]
	}
	return plantText
}

func (t Wechat) GetSessionKey(code string) (WxSessionKeyDto, error) {
	var sessionKeyDto WxSessionKeyDto
	httpState, bytes := t.get(fmt.Sprintf(SESSIONKEY_URL, t.AppId, t.AppSecret, code))
	if httpState != 200 {
		log.Printf("获取sessionKey失败,HTTP CODE:%d", httpState)
		return sessionKeyDto, errors.New("获取sessionKey失败")
	}
	e := json.Unmarshal(bytes, &sessionKeyDto)
	if e != nil {
		log.Printf("json解析失败", e)
		return sessionKeyDto, errors.New("json解析失败")
	}
	return sessionKeyDto, nil
}

func (t Wechat) DecryptPhoneData(phoneData, sessionKey, iv string) (string, error) {
	decrypt, err := t.AesDecrypt(phoneData, sessionKey, iv)
	if err != nil {
		log.Printf("解密数据失败", err)
		return "", err
	}
	var phoneDto = WxPhoneDto{}
	err = json.Unmarshal(decrypt, &phoneDto)
	if err != nil {
		log.Printf("解析手机号信息失败", err)
		return "", err
	}
	var phone = phoneDto.PurePhoneNumber
	return phone, nil
}
